package com.hastarin.android.udpsender; //package net.dinglisch.android.tasker; import java.net.URISyntaxException; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.util.Log; // Constants and functions for Tasker *extensions* to the plugin protocol // See Also: http://tasker.dinglisch.net/plugins.html // v1.0b6 public class TaskerPlugin { private final static String TAG = "TaskerPlugin"; private final static String BASE_KEY = "net.dinglisch.android.tasker"; private final static String EXTRAS_PREFIX = BASE_KEY + ".extras."; private final static int FIRST_ON_FIRE_VARIABLES_TASKER_VERSION = 80; /** * @see #addVariableBundle(Bundle, Bundle) * @see Host#getVariablesBundle(Intent) */ private final static String EXTRA_VARIABLES_BUNDLE = EXTRAS_PREFIX + "VARIABLES"; /** * Host capabilities, passed to plugin with condition or setting edit intents */ private final static String EXTRA_HOST_CAPABILITIES = EXTRAS_PREFIX + "HOST_CAPABILITIES"; /** * @see Setting#hostSupportsVariableReturn(Bundle) */ public final static int EXTRA_HOST_CAPABILITY_SETTING_RETURN_VARIABLES = 2; /** * @see Condition#hostSupportsVariableReturn(Bundle) */ public final static int EXTRA_HOST_CAPABILITY_CONDITION_RETURN_VARIABLES = 4; /** * @see Setting#hostSupportsOnFireVariableReplacement(Bundle) */ public final static int EXTRA_HOST_CAPABILITY_SETTING_FIRE_VARIABLE_REPLACEMENT = 8; /** * @see Setting#hostSupportsVariableReturn(Bundle) */ private final static int EXTRA_HOST_CAPABILITY_RELEVANT_VARIABLES = 16; /** * */ public final static int EXTRA_HOST_CAPABILITY_SETTING_SYNCHRONOUS_EXECUTION = 32; public final static int EXTRA_HOST_CAPABILITY_ALL = EXTRA_HOST_CAPABILITY_SETTING_RETURN_VARIABLES | EXTRA_HOST_CAPABILITY_CONDITION_RETURN_VARIABLES | EXTRA_HOST_CAPABILITY_SETTING_FIRE_VARIABLE_REPLACEMENT | EXTRA_HOST_CAPABILITY_RELEVANT_VARIABLES| EXTRA_HOST_CAPABILITY_SETTING_SYNCHRONOUS_EXECUTION; ; // /** * * @see #hostSupportsRelevantVariables(Bundle) * @see #addRelevantVariableList(Bundle, String[]) * @see #getRelevantVariableList(Bundle) */ private final static String BUNDLE_KEY_RELEVANT_VARIABLES = BASE_KEY + ".RELEVANT_VARIABLES"; public static boolean hostSupportsRelevantVariables( Bundle extrasFromHost ) { return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_RELEVANT_VARIABLES ); } /** * Specifies to host which variables might be used by the plugin. * * Used in EditActivity, before setResult(). * * @param intentToHost the intent being returned to the host * @param variableNames array of relevant variable names */ public static void addRelevantVariableList( Intent intentToHost, String [] variableNames ) { intentToHost.putExtra( BUNDLE_KEY_RELEVANT_VARIABLES, variableNames ); } /** * Allows the plugin/host to indicate to each other a set of variables which they are referencing. * The host may use this to e.g. show a variable selection list in it's UI. * The host should use this if it previously indicated to the plugin that it supports relevant vars * * @param fromHostIntentExtras usually from getIntent().getExtras() * @return variableNames an array of relevant variable names */ public static String [] getRelevantVariableList( Bundle fromHostIntentExtras ) { String [] relevantVars = (String []) getBundleValueSafe( fromHostIntentExtras, BUNDLE_KEY_RELEVANT_VARIABLES, String [].class, "getRelevantVariableList" ); if ( relevantVars == null ) relevantVars = new String [0]; return relevantVars; } /** * Used by: plugin QueryReceiver, FireReceiver * * Add a bundle of variable name/value pairs. * * @param resultExtras the result extras from the receiver onReceive (from a call to getResultExtras()) * @param variables the variables to send * @see #hostSupportsVariableReturn(Bundle) */ public static void addVariableBundle( Bundle resultExtras, Bundle variables ) { resultExtras.putBundle( EXTRA_VARIABLES_BUNDLE, variables ); } // ----------------------------- SETTING PLUGIN ONLY --------------------------------- // public static class Setting { /** * @see #setVariableReplaceKeys(Bundle, String[]) */ private final static String BUNDLE_KEY_VARIABLE_REPLACE_STRINGS = EXTRAS_PREFIX + "VARIABLE_REPLACE_KEYS"; /** * @see #requestTimeoutMS(Intent, int) */ private final static String EXTRA_REQUESTED_TIMEOUT = EXTRAS_PREFIX + "REQUESTED_TIMEOUT"; /** * @see #signalFinish(Context, Intent, Status, Bundle) * @see Host#addCompletionIntent(Intent, Intent) */ private final static String EXTRA_PLUGIN_COMPLETION_INTENT = EXTRAS_PREFIX + "COMPLETION_INTENT"; /** * @see #signalFinish(Context, Intent, Status, Bundle) * @see Host#getSettingCompletionStatus(Intent) */ public final static String EXTRA_RESULT_CODE = EXTRAS_PREFIX + "RESULT_CODE"; /** * @see #signalFinish(Context, Intent, Status, Bundle) * @see Host#getSettingResultCode(Intent) */ public final static int RESULT_CODE_OK = Activity.RESULT_OK; public final static int RESULT_CODE_OK_MINOR_FAILURES = Activity.RESULT_FIRST_USER; public final static int RESULT_CODE_FAILED = Activity.RESULT_FIRST_USER + 1; public final static int RESULT_CODE_PENDING = Activity.RESULT_FIRST_USER + 2; public final static int RESULT_CODE_UNKNOWN = Activity.RESULT_FIRST_USER + 3; /** * Used by: plugin EditActivity. * * Indicates to plugin that host will replace variables in specified bundle keys. * * Replacement takes place every time the setting is fired, before the bundle is * passed to the plugin FireReceiver. * * @param extrasFromHost intent extras from the intent received by the edit activity * @see #setVariableReplaceKeys(Bundle, String[]) */ public static boolean hostSupportsOnFireVariableReplacement( Bundle extrasFromHost ) { return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_SETTING_FIRE_VARIABLE_REPLACEMENT ); } /** * Used by: plugin EditActivity. * * Description as above. * * This version also includes backwards compatibility with pre 4.2 Tasker versions. * At some point this function will be deprecated. * * @param extrasFromHost intent extras from the intent received by the edit activity * @see #setVariableReplaceKeys(Bundle, String[]) */ public static boolean hostSupportsOnFireVariableReplacement( Activity editActivity ) { boolean supportedFlag = hostSupportsOnFireVariableReplacement( editActivity.getIntent().getExtras() ); if ( ! supportedFlag ) { String callerPackage = editActivity.getCallingActivity().getPackageName(); // Tasker only supporteed this from 1.0.10 supportedFlag = ( callerPackage.startsWith( BASE_KEY ) ) && ( getPackageVersionCode( editActivity.getPackageManager(), callerPackage ) > FIRST_ON_FIRE_VARIABLES_TASKER_VERSION ) ; } return supportedFlag; } public static boolean hostSupportsSynchronousExecution( Bundle extrasFromHost ) { return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_SETTING_SYNCHRONOUS_EXECUTION ); } /** * Request the host to wait the specified number of milliseconds before continuing. * Note that the host may choose to ignore the request. * * Used in EditActivity, before setResult(). * * @param intentToHost the intent being returned to the host * @param timeoutMS */ public static void requestTimeoutMS( Intent intentToHost, int timeoutMS ) { intentToHost.putExtra( EXTRA_REQUESTED_TIMEOUT, timeoutMS ); } /** * Used by: plugin EditActivity * * Indicates to host which bundle keys should be replaced. * * @param resultBundleToHost the bundle being returned to the host * @param listOfKeyNames which bundle keys to replace variables in when setting fires * @see #hostSupportsOnFireVariableReplacement(Bundle) */ public static void setVariableReplaceKeys( Bundle resultBundleToHost, String [] listOfKeyNames ) { StringBuilder builder = new StringBuilder(); if ( listOfKeyNames != null ) { for ( String keyName : listOfKeyNames ) { if ( keyName.contains( " " ) ) Log.w( TAG, "setVariableReplaceKeys: ignoring bad keyName containing space: " + keyName ); else { if ( builder.length() > 0 ) builder.append( ' ' ); builder.append( keyName ); } if ( builder.length() > 0 ) resultBundleToHost.putString( BUNDLE_KEY_VARIABLE_REPLACE_STRINGS, builder.toString() ); } } } /** * Used by: plugin FireReceiver * * Indicates to plugin whether the host will process variables which it passes back * * @param extrasFromHost intent extras from the intent received by the FireReceiver * @see #signalFinish(Context, Intent, Status, Bundle) */ public static boolean hostSupportsVariableReturn( Bundle extrasFromHost ) { return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_SETTING_RETURN_VARIABLES ); } /** * Used by: plugin FireReceiver * * Tell the host that the plugin has finished execution. * * @param originalFireIntent the intent received from the host (via onReceive()) * @param status level of success in performing the settings * @param vars any variables that the plugin wants to set in the host * @see #hostSupportsSynchronousSettings(Bundle) * @see #setWantSynchronousExecution(Intent, int) */ public static boolean signalFinish( Context context, Intent originalFireIntent, int resultCode, Bundle vars ) { String errorPrefix = "signalFinish: "; boolean okFlag = false; String completionIntentString = (String) TaskerPlugin.getExtraValueSafe( originalFireIntent, Setting.EXTRA_PLUGIN_COMPLETION_INTENT, String.class, "signalFinish" ); if ( completionIntentString != null ) { Uri completionIntentUri = null; try { completionIntentUri = Uri.parse( completionIntentString ); } // should only throw NullPointer but don't particularly trust it catch ( Exception e ) { Log.w( TAG, errorPrefix + "couldn't parse " + completionIntentString ); } if ( completionIntentUri != null ) { try { Intent completionIntent = Intent.parseUri( completionIntentString, Intent.URI_INTENT_SCHEME ); completionIntent.putExtra( EXTRA_RESULT_CODE, resultCode ); if ( vars != null ) completionIntent.putExtra( EXTRA_VARIABLES_BUNDLE, vars ); context.sendBroadcast( completionIntent ); okFlag = true; } catch ( URISyntaxException e ) { Log.w( TAG, errorPrefix + "bad URI: " + completionIntentUri ); } } } return okFlag; } } // ----------------------------- CONDITION PLUGIN ONLY --------------------------------- // public static class Condition { /** * Used by: plugin QueryReceiver * * Indicates to plugin whether the host will process variables which it passes back * * @param extrasFromHost intent extras from the intent received by the QueryReceiver * @see #addVariableBundle(Bundle, Bundle) */ public static boolean hostSupportsVariableReturn( Bundle extrasFromHost ) { return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_CONDITION_RETURN_VARIABLES ); } } // ---------------------------------- HOST ----------------------------------------- // public static class Host { /** * Tell the plugin what capabilities the host support. This should be called when sending * intents to any EditActivity, FireReceiver or QueryReceiver. * * @param toPlugin the intent we're sending * @return capabilites one or more of the EXTRA_HOST_CAPABILITY_XXX flags */ public static Intent addCapabilities( Intent toPlugin, int capabilities ) { return toPlugin.putExtra( EXTRA_HOST_CAPABILITIES, capabilities ); } /** * Add an intent to the fire intent before it goes to the plugin FireReceiver, which the plugin * can use to signal when it is finished. Only use if @code{pluginWantsSychronousExecution} is true. * * @param fireIntent fire intent going to the plugin * @param completionIntent intent which will signal the host that the plugin is finished. * Implementation is host-dependent. */ public static void addCompletionIntent( Intent fireIntent, Intent completionIntent ) { fireIntent.putExtra( Setting.EXTRA_PLUGIN_COMPLETION_INTENT, completionIntent.toUri( Intent.URI_INTENT_SCHEME ) ); } /** * When a setting plugin is finished, it sends the host the intent which was passed to it * via @code{addCompletionIntent}. * * @param completionIntent intent returned from the plugin when it finished. * @return completionStatus measure of plugin success, defaults to UNKNOWN */ public static int getSettingResultCode( Intent completionIntent ) { Integer val = (Integer) getExtraValueSafe( completionIntent, Setting.EXTRA_RESULT_CODE, Integer.class, "getSettingCompletionStatus" ); return ( val == null ) ? Setting.RESULT_CODE_UNKNOWN : val; } /** * Extract a bundle of variables from an intent received from the FireReceiver. This * should be called if the host previously indicated to the plugin * that it supports setting variable return. * * @param fromPlugin the intent we received * @return variables a bundle of variable name/value pairs * @see #addCapabilities(Intent, int) */ public static Bundle getVariablesBundle( Bundle resultExtras ) { return (Bundle) getBundleValueSafe( resultExtras, EXTRA_VARIABLES_BUNDLE, Bundle.class, "getVariablesBundle" ); } public static boolean haveRequestedTimeout( Bundle extrasFromPluginEditActivity ) { return extrasFromPluginEditActivity.containsKey( Setting.EXTRA_REQUESTED_TIMEOUT ); } public static int getRequestedTimeoutMS( Bundle extrasFromPluginEditActivity ) { return (Integer) getBundleValueSafe( extrasFromPluginEditActivity, Setting.EXTRA_REQUESTED_TIMEOUT, Integer.class, "getRequestedTimeout" ) ; } public static String [] getSettingVariableReplaceKeys( Bundle fromPluginEditActivity ) { String spec = (String) TaskerPlugin.getBundleValueSafe( fromPluginEditActivity, Setting.BUNDLE_KEY_VARIABLE_REPLACE_STRINGS, String.class, "getSettingVariableReplaceKeys" ); String [] replaceKeys = null; if ( spec != null ) replaceKeys = spec.split( " " ); return replaceKeys; } public static boolean haveRelevantVariables( Bundle b ) { return b.containsKey( BUNDLE_KEY_RELEVANT_VARIABLES ); } public static void cleanRelevantVariables( Bundle b ) { b.remove( BUNDLE_KEY_RELEVANT_VARIABLES ); } public static void cleanRequestedTimeout( Bundle extras ) { extras.remove( Setting.EXTRA_REQUESTED_TIMEOUT ); } public static void cleanSettingReplaceVariables( Bundle b ) { b.remove( Setting.BUNDLE_KEY_VARIABLE_REPLACE_STRINGS ); } } // ---------------------------------- HELPER FUNCTIONS -------------------------------- // private static Object getBundleValueSafe( Bundle b, String key, Class<?> expectedClass, String funcName ) { Object value = null; if ( b != null ) { if ( b.containsKey( key ) ) { Object obj = b.get( key ); if ( obj == null ) Log.w( TAG, funcName + ": " + key + ": null value" ); else if ( obj.getClass() != expectedClass ) Log.w( TAG, funcName + ": " + key + ": expected " + expectedClass.getClass().getName() + ", got " + obj.getClass().getName() ); else value = obj; } } return value; } private static Object getExtraValueSafe( Intent i, String key, Class<?> expectedClass, String funcName ) { return ( i.hasExtra( key ) ) ? getBundleValueSafe( i.getExtras(), key, expectedClass, funcName ) : null; } private static boolean hostSupports( Bundle extrasFromHost, int capabilityFlag ) { Integer flags = (Integer) getBundleValueSafe( extrasFromHost, EXTRA_HOST_CAPABILITIES, Integer.class, "hostSupports" ); return ( flags != null ) && ( ( flags & capabilityFlag ) > 0 ) ; } public static int getPackageVersionCode( PackageManager pm, String packageName ) { int code = -1; if ( pm != null ) { try { PackageInfo pi = pm.getPackageInfo( packageName, 0 ); if ( pi != null ) code = pi.versionCode; } catch ( Exception e ) { Log.e( TAG, "getPackageVersionCode: exception getting package info" ); } } return code; } }